//
//  TYRippleView.m
//  TYUIKit
//
//  Created by TuyaInc on 2018/11/6.
//

#import "TYRippleView.h"

#import "UIView+TYFrame.h"

@interface TYRippleLayerAnimationProxy : NSProxy <CAAnimationDelegate>

@property (nonatomic, weak) id<CAAnimationDelegate> target;
@property (nonatomic, weak) CALayer<TYRippleLayerProtocol> *layer;

@end

@implementation TYRippleLayerAnimationProxy

+ (instancetype)proxyWithTarget:(id<CAAnimationDelegate>)target layer:(CALayer<TYRippleLayerProtocol> *)layer {
    TYRippleLayerAnimationProxy *proxy = [TYRippleLayerAnimationProxy alloc];
    proxy.target = target;
    proxy.layer = layer;
    return proxy;
}

- (BOOL)isKindOfClass:(Class)aClass {
    return aClass == [TYRippleLayerAnimationProxy class] || [_target isKindOfClass:aClass];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_target respondsToSelector:aSelector];
}

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    if (_target && [_target respondsToSelector:sel]) {
        return [[_target class] methodSignatureForSelector:sel];
    } else {
        return [NSObject methodSignatureForSelector:@selector(init)];
    }
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    if (_target) {
        invocation.target = _target;
        [invocation invoke];
    }
}

@end



@interface TYRippleTimerProxy : NSProxy

@property (nullable, nonatomic, weak) id target;

+ (instancetype)proxyWithTarget:(nullable id)target;

@end

@implementation TYRippleTimerProxy

+ (instancetype)proxyWithTarget:(nullable id)target {
    TYRippleTimerProxy *proxy = [self alloc];
    proxy.target = target;
    return proxy;
}

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    void *null = NULL;
    [invocation setReturnValue:&null];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}

- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_target respondsToSelector:aSelector];
}

- (BOOL)isProxy {
    return YES;
}

@end




@interface TYRippleView () <CAAnimationDelegate>

@property (nonatomic) BOOL isWorking;

@property (nonatomic, strong) NSMutableArray<CALayer<TYRippleLayerProtocol> *> *idleLayerArray;
@property (nonatomic, strong) NSTimer *launchTimer;

@end

@implementation TYRippleView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initial];
    }
    return self;
}

- (instancetype)init {
    if (self = [super init]) {
        [self initial];
    }
    return self;
}

- (void)layoutSubviews {
    [super layoutSubviews];
    _emitterView.center = CGPointMake(self.ty_width * _emitterLocation.x, self.ty_height * _emitterLocation.y);
}

- (void)initial {
    _idleLayerArray = [NSMutableArray new];
    _lifeTime = 5;
    _birthRate = _lifeTime * 0.7;
    _emitterLocation = CGPointMake(0.5, 0.5);
    _groupDuration = _birthRate;
}

#pragma mark - Public
- (void)start {
    if (!_rippleLayerClass || (_launchTimer && [_launchTimer isValid])) {
        return;
    }
    
    _isWorking = YES;
    
    [self launchTimerAction];
    
    if (!_launchTimer) {
        CGFloat interval = _groupCount > 0 ? _groupDuration : _birthRate;
        _launchTimer = [NSTimer timerWithTimeInterval:interval target:[TYRippleTimerProxy proxyWithTarget:self] selector:@selector(launchTimerAction) userInfo:nil repeats:YES];
    }
    [[NSRunLoop currentRunLoop] addTimer:_launchTimer forMode:NSRunLoopCommonModes];
}

- (void)stop {
    [_launchTimer invalidate];
    _launchTimer = nil;
    _isWorking = NO;
}

#pragma mark - Private
- (void)launchTimerAction {
    if (_groupCount == 0) {
        [self birthRippleLayer];
    } else {
        __weak typeof(self) weakSelf = self;
        for (NSInteger idx = 0; idx < _groupCount; idx ++) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(idx * _birthRate * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [weakSelf birthRippleLayer];
            });
        }
    }
}

- (void)birthRippleLayer {
    CALayer<TYRippleLayerProtocol> *layer = [_idleLayerArray firstObject];
    if (layer) {
        [_idleLayerArray removeObjectAtIndex:0];
    } else {
        layer = [_rippleLayerClass layer];
    }
    
    layer.position = CGPointMake(self.bounds.size.width * _emitterLocation.x, self.bounds.size.height * _emitterLocation.y);
    
    if (_delegate && [_delegate respondsToSelector:@selector(rippleView:willLaunchLayer:)]) {
        [_delegate rippleView:self willLaunchLayer:layer];
    }
    
    if ([layer respondsToSelector:@selector(lifeAnimationForLayer:withRippleView:)]) {
        CAAnimation *anim = [layer lifeAnimationForLayer:_lifeTime withRippleView:self];
        
        TYRippleLayerAnimationProxy *animProxy = [TYRippleLayerAnimationProxy proxyWithTarget:self layer:layer];
        anim.delegate = animProxy;
        
        [layer addAnimation:anim forKey:@"ty_rippleLifeAnimation"];
    }
    
    [self.layer addSublayer:layer];

}

#pragma mark - Accessor
- (void)setEmitterView:(UIView *)emitterView {
    if (_emitterView != emitterView) {
        [_emitterView removeFromSuperview];

        _emitterView = emitterView;
        
        [self addSubview:emitterView];
        [self setNeedsLayout];
    }
}

- (void)setEmitterLocation:(CGPoint)emitterLocation {
    if (!CGPointEqualToPoint(emitterLocation, _emitterLocation)) {
        _emitterLocation = emitterLocation;
        [self setNeedsLayout];
    }
}

- (void)setGroupCount:(NSInteger)groupCount {
    if (!self.isWorking) {
        _groupCount = groupCount;
    }
}

- (void)setGroupDuration:(CGFloat)groupDuration {
    if (!self.isWorking) {
        _groupDuration = groupDuration;
    }
}

#pragma mark - <CAAnimationDelegate>
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    if ([anim.delegate isKindOfClass:[TYRippleLayerAnimationProxy class]]) {
        TYRippleLayerAnimationProxy *animProxy = (TYRippleLayerAnimationProxy *)anim.delegate;
        CALayer<TYRippleLayerProtocol> *layer = animProxy.layer;
        if (layer) {
            [layer removeFromSuperlayer];
            [layer removeAllAnimations];
            [_idleLayerArray addObject:layer];
        }
    }
}

@end
